AWS Transfer Family の サーバー + ユーザー + ワークフロー 一式をCloudFormationで作成する
いわさです。
Transfer Family の マネージドワークフローを構築する機会が最近多くて、サーバーとユーザー含めて作っては壊してってのを繰り返しているのですが、さすがにCloudFormationテンプレートでも用意するか...となりました。
そして、マネージドワークフローのテンプレートがまだ記事で見当たらなかったので一式を公開したいと思います。
作るもの
サーバープロトコルはSFTPで、IDプロバイダーはサービスマネージドのものを構築します。
そして、エンドポイントタイプはVPCでパブリックIPを付与しており、ローカルからのみにIPアドレス制限を行えるように構成してみます。
ワークフローは、実はステップの種類ごとに設定方法が異なるので、コピー・カスタム・削除の3つのステップを組み合わせてみました。
作ったもの
まず、テンプレート全体はこちらのリポジトリに置いておきます。
サーバー
Transfer Family サーバー本体の部分です。
EndpointDetails
でVPCの構成およびIPアドレスの関連付けを行っています。
サービスマネージドユーザーを使うので、IdentityProviderType
のみ指定しています。
Transfer Familyは自動でCloudWatch Logsへログ出力してくれるので、AWSTransferLoggingAccess
マネージドポリシーをアタッチしたIAMロールを指定しています。
後ほどワークフローの中身を見ていきますが、サーバーのWorkflowDetails
でワークフローIDとワークフロー実行ロールを指定します。
CloudFormationで今回実装して気づいたんですが、OnUpload
というトリガーに設定を行っていますね。
本日時点ではファイルアップロード時のみワークフローが実行されますが、将来別のトリガーも実装される可能性を感じます。楽しみです。
: SFTP: Type: AWS::Transfer::Server Properties: Domain: S3 Protocols: - SFTP EndpointType: VPC EndpointDetails: AddressAllocationIds: - !GetAtt EipTransfer.AllocationId SecurityGroupIds: - !Ref SecurityGroup SubnetIds: - !Ref PublicSubnet1 VpcId: !Ref VPC IdentityProviderType: SERVICE_MANAGED LoggingRole: !GetAtt TransferLoggingRole.Arn WorkflowDetails: OnUpload: - WorkflowId: !Ref Workflow ExecutionRole: !GetAtt WorkflowExecutionRole.Arn : TransferLoggingRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: transfer.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSTransferLoggingAccess :
ユーザー
こちらはユーザーを作成してます。
スタック作成時のパラメータで公開鍵を設定するようにしています。
テンプレートから切り離すことで複数ユーザー向けに汎用的に利用出来そうです。
Transfer Familyユーザーに、S3バケットへのアクセス権限をIAMロールで渡す必要があります。
ここではさぼってAmazonS3FullAccess
を指定しまっているのでご注意ください。
実際には、対象のS3バケットやプレフィックスのみなど絞り込むべきです。
Parameters: : UserName: Type: String Default: user1 UserPublicKey: Type: String Resources: : TransferUser: Type: AWS::Transfer::User Properties: ServerId: !GetAtt SFTP.ServerId HomeDirectory: !Sub "/${S3Bucket}/${UserName}" HomeDirectoryType: "PATH" Role: !GetAtt TransferExecutionRole.Arn UserName: !Ref UserName SshPublicKeys: - !Ref UserPublicKey TransferExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: transfer.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonS3FullAccess S3Bucket: Type: AWS::S3::Bucket Properties: AccessControl: Private BucketName: !Sub s3-${AWS::StackName}-${AWS::AccountId}-${AWS::Region} PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true :
ワークフロー
ここでは以前ご紹介した新機能のマネージドワークフローを構成しています。
Steps
プロパティで複数のステップを構成していて、それぞれにTYPE
とxxxStepDetails
を指定しています。
このStepDetailはTYPEごとに違っていて、かつドキュメントではJSON形式との指定がありますが、具体的なパラメータが記載されていません。
このあたりはCloudFormationのテンプレートリファレンスよりも、ユーザーガイドのAPIリファレンスが参考になります。
CreateWorkflow-AWS Transfer Family
マネージドワークフローではふたつのIAMロールが登場します。
ひとつはワークフローの実行ロールです。ここではS3バケットのファイルコピーと削除、そしてLambdaの実行が出来る必要があります。
ふたつめはLambda関数の実行ロールです。
カスタムステップのLambda関数では、Transfer Familyに対して、ワークフローステップのステータスを送信してやる必要があります。CloudFormationのカスタムリソースに似ています。
ですので、最低でもTransfer Familyへのsend_workflow_step_state
が出来る権限が必要です。
: Workflow: Type: AWS::Transfer::Workflow Properties: Steps: - Type: COPY CopyStepDetails: Name: !Sub ${AWS::StackName}-copy-step DestinationFileLocation: S3FileLocation: Bucket: !Ref S3Bucket Key: "${transfer:UserName}/copy.dat" OverwriteExisting: "TRUE" SourceFileLocation: "${previous.file}" - Type: CUSTOM CustomStepDetails: Name: !Sub ${AWS::StackName}-custom-step Target: !GetAtt lambdaFunction.Arn TimeoutSeconds: 60 SourceFileLocation: "${previous.file}" - Type: DELETE DeleteStepDetails: Name: !Sub ${AWS::StackName}-delete-step SourceFileLocation: "${original.file}" WorkflowExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: transfer.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaRole - arn:aws:iam::aws:policy/AmazonS3FullAccess lambdaFunction: Type: AWS::Lambda::Function Properties: FunctionName: !Sub ${AWS::StackName}-transfer-customstep Role: !GetAtt lambdaExecutionRole.Arn Handler: index.lambda_handler Runtime: python3.8 Code: ZipFile: | import json import boto3 transfer = boto3.client('transfer') def lambda_handler(event, context): print(json.dumps(event)) transfer_response = transfer.send_workflow_step_state( WorkflowId=event['serviceMetadata']['executionDetails']['workflowId'], ExecutionId=event['serviceMetadata']['executionDetails']['executionId'], Token=event['token'], Status='SUCCESS' ) return { 'statusCode': 200, 'body': json.dumps(transfer_response) } lambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/AWSTransferFullAccess :
デプロイと実行
リソースは期待どおり作成されていますね。
SFTPクライアントからファイルをアップロードしてみます。
$ sftp -i user1 user1@52.192.236.242 Connected to 52.192.236.242. sftp> put hoge1.txt Uploading hoge1.txt to /s3-hogesftp-123456789012-ap-northeast-1/user1/hoge1.txt hoge1.txt 100% 15 0.4KB/s 00:00
ファイルがアップロードされ、マネージドワークフローが動作してオリジンファイルが削除されていますね。ヨシ!
さいごに
本日は、Transfer Family の CloudFormation 対応リソース一式を作ってデプロイしてみました。
Transfer Familyで構築したい時はとりあえずこのテンプレートをベースに修正していってみようかなと思ってます。
途中触れていますが、注意事項があって、IAMロールが登場しすぎてマネージドポリシーばかり使ってしまいました。マネージドポリシーが悪いというより権限が大きいまま放置しているので良くないですね。
もし参考にされる方がいらっしゃいましたら、その点ご認識頂き必要に応じてより厳格なポリシーを付与して頂ければと思います。